﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace RefOffDesktop
{
    /*
     * Class: Haversine
     * 
     * Use: Calculates the distance between a Whistle event and
     *      a Caution or Sending Off incident within a Match
     *      
     * Author: Mike Skaife, mcs3@aber.ac.uk
     */ 
    public class Haversine
    {
        //Variables used in calculations to represent longitudes
        //and latitudes of the two GPS locations in decimal form
        double latDecimal1;
        double longDecimal1;
        double latDecimal2;
        double longDecimal2;

        //Variable to hold the calculated distance
        String distance;
        
        public Haversine()
        {
            //Initialise the variables
            latDecimal1 = 0;
            longDecimal1 = 0;
            latDecimal2 = 0;
            longDecimal2 = 0;
        }

        //Method used to calculate the distance between the two GPS locations,
        //based on their latitudes and longitudes. This method was taken from code
        //written by Gary Dryden, and freely available at:
        //
        //http://www.codeproject.com/KB/cs/distancebetweenlocations.aspx
        //
        //Modifications are indicated within the code below
        public double Calc(double Lat1, double Long1, double Lat2, double Long2)
        {
            /*
                The Haversine formula according to Dr. Math.
                http://mathforum.org/library/drmath/view/51879.html
            
                dlon = lon2 - lon1
                dlat = lat2 - lat1
                a = (sin(dlat/2))^2 + cos(lat1) * cos(lat2) * (sin(dlon/2))^2
                c = 2 * atan2(sqrt(a), sqrt(1-a)) 
                d = R * c
            
                Where
                    * dlon is the change in longitude
                    * dlat is the change in latitude
                    * c is the great circle distance in Radians.
                    * R is the radius of a spherical Earth.
                    * The locations of the two points in 
                        spherical coordinates (longitude and 
                        latitude) are lon1,lat1 and lon2, lat2.
            */
            double dDistance = Double.MinValue;
            double dLat1InRad = Lat1 * (Math.PI / 180.0);
            double dLong1InRad = Long1 * (Math.PI / 180.0);
            double dLat2InRad = Lat2 * (Math.PI / 180.0);
            double dLong2InRad = Long2 * (Math.PI / 180.0);

            double dLongitude = dLong2InRad - dLong1InRad;
            double dLatitude = dLat2InRad - dLat1InRad;

            // Intermediate result a.

            double a = Math.Pow(Math.Sin(dLatitude / 2.0), 2.0) +
                       Math.Cos(dLat1InRad) * Math.Cos(dLat2InRad) *
                       Math.Pow(Math.Sin(dLongitude / 2.0), 2.0);

            // Intermediate result c (great circle distance in Radians).

            double c = 2.0 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1.0 - a));

            // Distance.

            // const Double kEarthRadiusMiles = 3956.0;

            const Double kEarthRadiusKms = 6376.5;

            //MODIFICATION: Result multiplied by 1000 to give
            //              distance result in metres rather than
            //              kilometres
            dDistance = kEarthRadiusKms * c * 1000;

            return dDistance;
        }

        //Method used to convert a GPS latitude into decimal format,
        //required for use in the actual Haversine calculation
        public double convertLatToDecimal(String gps)
        {
            //Variable to store the result
            double latDecimal = 0;

            //Check that a valid GPS sentence has been
            //passed to the method
            if(!(gps.Equals("")))
            {            
                //Split the GPS sentence into its separate 'words'
                string[] Words = gps.Split(',');

                //The fourth word in the sentence is the latitude value, which
                //is split here into its constituent parts - Degrees, Minutes and
                //Seconds
                int latDegreesInt = Int32.Parse(Words[3].Substring(0,2));
                int latMinsInt = Int32.Parse(Words[3].Substring(2,2));
                int latSecsInt = Int32.Parse(Words[3].Substring(5,4));

                //These parts are then converted into doubles, to
                //ultimately allow them to be recorded as decimal numbers
                double latDegrees = (double)latDegreesInt;
                double latMins = (double)latMinsInt;
                double latSecs = (double)latSecsInt;
                            
                //The fifth word in the sentence indicates the hemisphere for
                //the latitude. If this is 'S', or south, then the value of
                //the decimal latitude must be negated. Here the decimal value
                //of the latitude is calculated as: Degrees + (Mins/60) + (Secs/3600)
                if (Words[4].Equals("S"))
                {
                    latDecimal = -(latDegrees + (latMins / 60) + (latSecs / 3600));
                }
                else
                {
                    latDecimal = latDegrees + (latMins / 60) + (latSecs / 3600);
                }
            }

            //Return the latitude of the original GPS sentence
            //in decimal format
            return latDecimal;
        }

        //Method used to convert a GPS longitude into decimal format,
        //required for use in the actual Haversine calculation
        public double convertLongToDecimal(String gps)
        {
            //Variable to store the result
            double longDecimal = 0;

            //Check that a valid GPS sentence has been
            //passed to the method
            if (!(gps.Equals("")))
            {
                //Split the GPS sentence into its separate 'words'
                string[] Words = gps.Split(',');

                //The sixth word in the sentence is the longitude value, which
                //is split here into its constituent parts - Degrees, Minutes and
                //Seconds
                int longDegreesInt = Int32.Parse(Words[5].Substring(0, 3));
                int longMinsInt = Int32.Parse(Words[5].Substring(3, 2));
                int longSecsInt = Int32.Parse(Words[5].Substring(6, 4));

                //These parts are then converted into doubles, to
                //ultimately allow them to be recorded as decimal numbers
                double longDegrees = (double)longDegreesInt;
                double longMins = (double)longMinsInt;
                double longSecs = (double)longSecsInt;

                //The seventh word in the sentence indicates the hemisphere for
                //the longitude. If this is 'W', or west, then the value of
                //the decimal longitude must be negated. Here the decimal value
                //of the longitude is calculated as: Degrees + (Mins/60) + (Secs/3600)
                if (Words[4].Equals("W"))
                {
                    longDecimal = -(longDegrees + (longMins / 60) + (longSecs / 3600));
                }
                else
                {
                    longDecimal = longDegrees + (longMins / 60) + (longSecs / 3600);
                }
            }

            //Return the latitude of the original GPS sentence
            //in decimal format
            return longDecimal;
        }

        //Method called by external classes to obtain the distance in
        //metres between two specified GPS locations
        public String getDistance(String gps1, String gps2)
        {
            //Variables holding the two GPS sentences involved
            String GPS1 = gps1;
            String GPS2 = gps2;

            //Obtain decimal values for the longitude and latitude of
            //each of the GPS sentences
            latDecimal1 = convertLatToDecimal(GPS1);
            longDecimal1 = convertLongToDecimal(GPS1);
            latDecimal2 = convertLatToDecimal(GPS2);
            longDecimal2 = convertLongToDecimal(GPS2);

            //Calculate the distance between the two locations using these
            //decimal longitudes and latitudes
            double calculatedDistance = Calc(latDecimal1, longDecimal1, latDecimal2, longDecimal2);

            //Set the distance between the two locations as a
            //String, formatted to two decimal places
            distance = String.Format("{0:0.00}", calculatedDistance);

            //Return the distance between the two GPS locations
            return distance;
        }            
    }
}
